import { ArrowLeftRight } from 'lucide-react'; import { ScrollX } from '@/components/models/scroll-x'; import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { ComparabilityBadge, TrustBadge } from '@/components/models/badges'; import { fmtScoreUnit } from '@/components/models/shared'; import { Chip, EntityBadge } from '@/components/ui/badges'; import { DataTable, Td, Th } from '@/components/ui/data-table'; import { EntityLink } from '@/components/ui/entity'; import { Container, Note, PageHeader } from '@/components/ui/section'; import { EmptyState, Unavailable } from '@/components/ui/unavailable'; import { ApiError, apiD1, safe } from '@/lib/api'; import { cn } from '@/lib/cn'; import { fmtDate, fmtInt, fmtTokens, fmtUsdPerM, fmtValue, num } from '@/lib/format'; import { routes, SITE_NAME, SITE_URL } from '@/lib/site'; import type { DiffDelta, ModelDiffPayload } from '@/lib/types'; /* /models/[a]/diff/[b] — only the dimensions whose observed values differ, with deltas: context 200K → 1M (+400%), price −34%, benchmark ± points (comparable groups only), capability lists as added / removed. Shareable; linked from /compare. */ type Params = { params: Promise<{ slug: string; b: string }> }; export const revalidate = 300; async function load(a: string, b: string): Promise<{ res: ModelDiffPayload | null; error: string | null }> { try { return { res: await apiD1.modelDiff(a, b), error: null }; } catch (e) { if (e instanceof ApiError && e.notFound) notFound(); if (e instanceof ApiError && (e.status === 400 || e.status === 422)) return { res: null, error: e.detail ?? 'These two entities cannot be diffed.' }; return { res: null, error: null }; } } export async function generateMetadata({ params }: Params): Promise { const { slug, b } = await params; const res = await safe(apiD1.modelDiff(slug, b)); const canonical = `/models/${encodeURIComponent(slug)}/diff/${encodeURIComponent(b)}`; if (!res) return { title: 'Model diff', robots: { index: false }, alternates: { canonical } }; const title = `${res.a.name} vs ${res.b.name} — what differs`; const description = `${res.dimensions.length} differing dimensions between ${res.a.name} and ${res.b.name}: context, prices, benchmarks (comparable groups only), capabilities — each value with its source. ${SITE_NAME}.`; return { title, description, alternates: { canonical }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${canonical}`, type: 'article' } }; } function isNumDelta(d: DiffDelta): d is { absolute: number; percent: number | null } { return !!d && 'absolute' in d; } function isListDelta(d: DiffDelta): d is { added: unknown[]; removed: unknown[] } { return !!d && 'added' in d; } function cell(kind: string, key: string, v: unknown, unit?: string): string { if (v === null || v === undefined || v === '' || (Array.isArray(v) && v.length === 0)) return 'Unavailable'; if (kind === 'date') return fmtDate(String(v)); if (kind === 'bool') return v ? 'Yes' : 'No'; if (kind === 'list') return (Array.isArray(v) ? v : [v]).map(String).join(', '); if (kind === 'number') { if (/per_mtok/.test(key)) return fmtUsdPerM(v); if (key.startsWith('bench:')) return fmtScoreUnit(num(v), unit ?? null); if (/context_length|max_output_tokens/.test(key)) return fmtTokens(v); return fmtValue(v, key); } return typeof v === 'string' ? v : fmtValue(v, key); } /** "+39 days" / "−2 months" between two ISO dates (month precision when either date is YYYY-MM). */ function dayDelta(a: string, b: string): string { const monthly = a.length <= 7 || b.length <= 7; const ta = new Date(a.length === 7 ? `${a}-01` : a.length === 4 ? `${a}-01-01` : a).getTime(); const tb = new Date(b.length === 7 ? `${b}-01` : b.length === 4 ? `${b}-01-01` : b).getTime(); if (!Number.isFinite(ta) || !Number.isFinite(tb)) return 'differs'; const days = Math.round((tb - ta) / 86400000); const sign = days > 0 ? '+' : '−'; const abs = Math.abs(days); if (monthly || abs >= 90) { const months = Math.round(abs / 30.44); return `${sign}${months} month${months === 1 ? '' : 's'}`; } return `${sign}${abs} day${abs === 1 ? '' : 's'}`; } function fmtPct(p: number | null): string | null { if (p === null || !Number.isFinite(p)) return null; const sign = p > 0 ? '+' : p < 0 ? '−' : ''; const abs = Math.abs(p); return `${sign}${abs >= 100 ? abs.toFixed(0) : abs.toFixed(abs < 10 ? 1 : 0)}%`; } export default async function ModelDiffPage({ params }: Params) { const { slug, b } = await params; const { res, error } = await load(slug, b); const canonical = `/models/${encodeURIComponent(slug)}/diff/${encodeURIComponent(b)}`; if (!res) return (
{error ? {error} : }
); const attr = res.dimensions.filter((d) => d.source !== 'results' && d.source !== 'prices'); const prices = res.dimensions.filter((d) => d.source === 'prices'); const bench = res.dimensions.filter((d) => d.source === 'results'); const lists = res.dimensions.filter((d) => isListDelta(d.delta)); const ld = { '@context': 'https://schema.org', '@type': 'Dataset', name: `${res.a.name} vs ${res.b.name} — differences`, url: `${SITE_URL}${canonical}`, description: res.note ?? undefined, about: [res.a, res.b].map((e) => ({ '@type': 'SoftwareApplication', name: e.name, url: `${SITE_URL}${routes.entity(e)}` })) }; const Row = ({ d }: { d: ModelDiffPayload['dimensions'][number] }) => { const comp = res.comparability?.[d.key]; const nd = isNumDelta(d.delta) ? d.delta : null; const better = nd && d.kind === 'number' ? (d.source === 'prices' ? nd.absolute < 0 : (d.higher_is_better ?? true) ? nd.absolute > 0 : nd.absolute < 0) : null; return ( {d.key.startsWith('bench:') && d.benchmark ? ( {d.label.split(' · ')[0]} ) : ( d.label )} {d.key.startsWith('bench:') ? d.label.split(' · ').slice(1).join(' · ') : d.unit} {comp && ( {comp.trust?.[res.a.id] && } )} {cell(d.kind, d.key, d.a, d.unit)} {cell(d.kind, d.key, d.b, d.unit)} {nd ? ( {d.source === 'prices' ? `${nd.absolute > 0 ? '+' : '−'}${fmtUsdPerM(Math.abs(nd.absolute))}` : d.key.startsWith('bench:') ? `${nd.absolute > 0 ? '+' : '−'}${Math.abs(nd.absolute).toFixed(Math.abs(nd.absolute) < 10 ? 2 : 1)}${d.unit === '%' ? ' pt' : ''}` : `${nd.absolute > 0 ? '+' : '−'}${fmtValue(Math.abs(nd.absolute), d.key)}`} {fmtPct(nd.percent) && {fmtPct(nd.percent)}} ) : isListDelta(d.delta) ? ( {d.delta.added.map((x) => ( + {String(x)} ))} {d.delta.removed.map((x) => ( − {String(x)} ))} ) : d.kind === 'date' && typeof d.a === 'string' && typeof d.b === 'string' ? ( {dayDelta(d.a, d.b)} ) : ( differs )} ); }; const Table = ({ rows, caption }: { rows: ModelDiffPayload['dimensions']; caption: string }) => ( Dimension {res.a.name} {res.b.name} Delta (b − a) {rows.map((d) => ( ))} ); return (